//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include <Windows.h>
#include <stdlib.h>
#include <memory>
#include "appbase3.h"
#include "rend_system.h"
#include "rendm5d.h"
#include "gxy.h"
#include "SkyBox.h"
#include "SeaBed.h"
#include "CamFly.h"

//==================================================================
using namespace PMath;

//==================================================================
///
//==================================================================
class FlyTest : public Application
{
public:
	autodel<Window>	_winp;

	RendM5D::Scene		*_r5scenep;
	RendDevice			*_rdevicep;
	GXY::Context		*_mygxy_ctxap;

	M5D::Camera			*_camerap;
	M5D::Light			*_lightp;
	autodel<SkyBox>	_skyboxp;
	autodel<SeaBed>	_seabedp;
	CamFly				_camfly;

	FlyTest() :
		_r5scenep(NULL),
		_rdevicep(NULL),
		_mygxy_ctxap(NULL)
	{
	}

	//==================================================================
	bool NeedQuit() const;

	//==================================================================
	static int	handleWinEvent_s( void *objectp, WindowEvent *eventp )
	{
		return ((FlyTest *)objectp)->handleWinEvent( eventp );
	}
	//==================================================================
	int	handleWinEvent( WindowEvent *eventp );

	//==================================================================
	void doPaint( int vp_wd, int vp_he );
	bool Idle();
	void onCreation( int vp_wd, int vp_he );
};

//==================================================================
///
//==================================================================
static FlyTest	_app;

//==================================================================
bool main_start( void *hinstance )
{
	u_int	desk_wd, desk_he;

	WinSys::Init( (HINSTANCE)hinstance, NULL, _T("SHATEST") );
	WinSys::GetDeskSize( desk_wd, desk_he );

	int	win_wd = desk_wd * 3 / 4;
	int	win_he = desk_he * 3 / 4;

	_app._winp = new Window( _T("FlyTest - Pascalibs demo by Davide Pasca"),
							 NULL,
							 &_app,
							 FlyTest::handleWinEvent_s, 
							 (desk_wd - win_wd) / 2,
							 (desk_he - win_he) / 2,
							 win_wd,
							 win_he,
							 (win_init_flags)(WIN_INIT_FLG_OPENGL | WIN_INIT_FLG_DONT_CLEAR) );

	return true;
}

//==================================================================
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	PSYS::Initialize( NULL );
	HICON	hicon = NULL;//LoadIcon( hInstance, MAKEINTRESOURCE(IDI_ICO_APPL) );
	WinSys::Init( hInstance, hicon, _T("FLIGHTTEST") );

	//ss_init_winsock();

	u_int	desk_wd, desk_he;
	WinSys::GetDeskSize( desk_wd, desk_he );

	int	win_wd = desk_wd * 3 / 4;
	int	win_he = desk_he * 3 / 4;

	_app._winp = new Window( _T("FlyTest - Pascalibs demo by Davide Pasca"),
							 NULL,
							 &_app,
							 FlyTest::handleWinEvent_s, 
							 (desk_wd - win_wd) / 2,
							 (desk_he - win_he) / 2,
							 win_wd,
							 win_he,
							 (win_init_flags)(WIN_INIT_FLG_OPENGL | WIN_INIT_FLG_DONT_CLEAR) );

	while (1)
	{
		if ( _app.Idle() )
			break;

		_app._winp->Invalidate();

		//if ( _basechannel->Idle() || _basechannel->NeedQuit() )
		//	break;

		//Sleep( 20 );
	}

	//if ( _basechannel )
	//	delete _basechannel;

	return 0;
}

//==================================================================
bool FlyTest::NeedQuit() const
{
	return false;
}

//==================================================================
void FlyTest::doPaint( int vp_wd, int vp_he )
{
	_r5scenep->SelectCamera( _camerap );
	_r5scenep->UpdatePose();
	_camerap->SetViewport( 0, 0, vp_wd, vp_he, 0, 1 );

	RendM5D::Params	rparams;
	rparams.use_lights = true;
	_r5scenep->PreRender( rparams );
	
	_rdevicep->RenderBegin();
		_r5scenep->Render( rparams );
/*
		GXY::SelectContext( _mygxy_ctxap );
		GXY::SetViewport( 0, 0, vp_wd, vp_he );
		GXY::SelectTexture( 0 );
		GXY::PrimBegin( GXY::PRIM_TRIGS );
		GXY::SetVertRGB( 255, 0, 0 );	GXY::SetVertPos( vp_wd/2, 10 );
		GXY::SetVertRGB( 0, 255, 0 );	GXY::SetVertPos( vp_wd-100, vp_he-100 );
		GXY::SetVertRGB( 0, 0, 255 );	GXY::SetVertPos( 100, vp_he-100 );
		GXY::PrimEnd();
		GXY::Flush();
*/
	_rdevicep->RenderEnd();
}

//==================================================================
void FlyTest::onCreation( int vp_wd, int vp_he )
{
	_rdevicep = RendSystem::NewRendDevice_s( vp_wd, vp_he, _T(".") );
	_mygxy_ctxap = GXY::CreateContext( _rdevicep, vp_wd, vp_he, (float)vp_wd / vp_he );

	_r5scenep = new RendM5D::Scene( _rdevicep, "FlyTest Scene", _T(".") );

	_lightp = _r5scenep->NewLight( "Sun light" );
	_lightp->SetColor( Vector3( 8, 8, 8 ) );
	Vector4	ldir = Vector4( .69f, .7f, .65f, 0).GetNormalized3();
	_lightp->SetLocalPos( ldir );

	// camera for the scene
	_camerap = _r5scenep->NewCamera( "Camera" );
	_camerap->SetViewport( 0, 0, vp_wd, vp_he, 0, 1 );
	_camerap->SetPerspectiveProj( M_PI/3, 1.77f, 1, 40000 );

	_skyboxp = new SkyBox( _r5scenep );
	_skyboxp->Rebuild();

	_seabedp = new SeaBed( _r5scenep );

	_camfly.Setup( 0, 25, 300, 0, 0, 0 );
}

//==================================================================
bool FlyTest::Idle()
{
	if ( Application::Idle() )
		return true;


	float	lstick[2];
	float	rstick[2];

	//gamepadp->GetSticks( 0, lstick, rstick );

	lstick[0] = (GetAsyncKeyState( 'A' ) & 0x8000) ?  -1 :
				((GetAsyncKeyState( 'D' ) & 0x8000) ?  1 :
				0);


	lstick[1] = (GetAsyncKeyState( 'W' ) & 0x8000) ?  1 :
				((GetAsyncKeyState( 'S' ) & 0x8000) ? -1 :
				0);

	rstick[0] = (GetAsyncKeyState( VK_LEFT ) & 0x8000) ?  -1 :
				((GetAsyncKeyState( VK_RIGHT ) & 0x8000) ?  1 :
				0);


	rstick[1] = (GetAsyncKeyState( VK_UP ) & 0x8000) ?  1 :
				((GetAsyncKeyState( VK_DOWN ) & 0x8000) ? -1 :
				0);

	_camfly.Update( lstick[0]*5, 0, -lstick[1]*5, -rstick[1] * M_PI/180, -rstick[0] * M_PI/180, 0 );

	if ( _skyboxp )		_skyboxp->Update( *_camfly.GetMatrix().GetRow3(3) );
	if ( _seabedp )		_seabedp->Update();

	// select the camera for the render-scene
	_camerap->SetMatrix( _camfly.GetMatrix().GetInverse() );

	return false;
}

//==================================================================
int	FlyTest::handleWinEvent( WindowEvent *eventp )
{
	switch ( eventp->GetType() )
	{
	case WindowEvent::ETYPE_CREATE:
		onCreation( eventp->winp->GetWidth(), eventp->winp->GetHeight() );
		break;

	case WindowEvent::ETYPE_PAINT:
		doPaint( eventp->winp->GetWidth(), eventp->winp->GetHeight() );
		return 1;

	case WindowEvent::ETYPE_WINRESIZE:
		doPaint( eventp->winp->GetWidth(), eventp->winp->GetHeight() );
		return 1;

	case WindowEvent::ETYPE_DESTROY:
		PostQuitMessage( 0 );
		break;
	}

	return 0;
}
